package engine;

import java.io.IOException;
import java.util.List;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import primitives.Patch;
import primitives.Shape;

import com.google.common.annotations.VisibleForTesting;

import engine.io.PNGWriter;
import engine.io.ShapeReader;

public class Core {
	
	public static final int FACES_WIDE = 20;
	public static final int IMG_WIDTH = 340;
	public static final int IMG_HEIGHT = 340;
	public static final DeltaFormFactors DFF = new DeltaFormFactors();
	public static RayCaster rc = new RayCaster();
	public static List<Shape> shapes;
	
	private ShapeReader reader;
	private PNGWriter writer;
	private List<Patch> patches;
	
	@VisibleForTesting
	protected int PASSES = 50;
	
	public Core() {
		reader = new ShapeReader();
		writer = new PNGWriter();
	}

	public void run(String inputFile) {
		long start = System.currentTimeMillis();

		setShapesFromFile(inputFile);
		setFormFactors();
		for(int i = 1; i <= PASSES; i++) {
			System.out.println("Performing pass " + i + "...");
			performPass();
			double[][] grayscale = rc.run(shapes, IMG_WIDTH, IMG_HEIGHT);
			int[][] pixels = clamp(grayscale);
			writer.createPNG(pixels, "outputPass" + i + ".png", 
					IMG_WIDTH, IMG_HEIGHT);
		} 
		
		long end = System.currentTimeMillis();
		System.out.println("Took " + (end - start) + " ms to complete.");
	}
	
	private void setShapesFromFile(String filename) {
		Document doc = null;
		try {
			doc = reader.getDocument(filename);
		} catch (SAXException e) {
			System.err.println("Can't use file as input for radioisty engine.");
			System.exit(-1);
		} catch (IOException e) {
			System.err.println("IOException reading file: " + e.getMessage());
			System.exit(-1);
		}
		shapes = reader.getShapes(doc);
		patches = reader.getPatches();
	}
	
	public void setFormFactors() {
		System.out.println("Setting form factors...");
		System.out.println("_____________________");
		int chunk = patches.size() / 20;
		int count = 0;
		int progress = 0;
		for(Patch patch : patches) {
			patch.setHemicube();
			patch.shoot();
			count++;
			if(count / chunk == progress) {
				System.out.print("|");
				progress++;
			}
		}
		System.out.println("\n");
	}
	
	public void performPass() {
		for (Patch patch : patches) {
			patch.gather();
		}
		for (Patch patch : patches) {
			patch.shoot();
		}
	}
	
	private int[][] clamp(double[][] grayscaleIntensities) {
		int[][] pixels = new int[IMG_WIDTH][IMG_HEIGHT];
		double max = Double.MIN_VALUE;
		double min = Double.MAX_VALUE;
		for (int x = 0; x < IMG_WIDTH; x++) {
			for (int y = 0; y < IMG_HEIGHT; y++) {
				if (grayscaleIntensities[x][y] > max
						&& Math.abs(grayscaleIntensities[x][y] - 1) > 0.0001) {
					max = grayscaleIntensities[x][y];
				}
				if (grayscaleIntensities[x][y] < min) {
					min = grayscaleIntensities[x][y];
				}
			}
		}
		System.out.println("min = " + min);
		System.out.println("max = " + max);
		for (int x = 0; x < IMG_WIDTH; x++) {
			for (int y = 0; y < IMG_HEIGHT; y++) {
				double numerator = grayscaleIntensities[x][y] - min;
				double denominator = max - min;
				int value = (int)((numerator / denominator) * 255.0);
				//int value = (int)(grayscaleIntensities[x][y] * 255.0);
				/*if(value < 0 || value > 255) {
					System.err.println("error here: value = " + 
							grayscaleIntensities[x][y] + ", min = " + min + 
							", numerator = " + numerator + ", denominator = " + 
							denominator);
				}*/
				value = (value > 255) ? 255 : value;
				value = (value < 0) ? 0 : value;
				pixels[x][y] = value;
			}
		}
		return pixels;
	}
	
	public static void main(String[] args) {
		Core core = new Core();	
		core.run("C:\\Users\\Brittany\\Documents\\input2.xml");
	}
}